/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package br.com.jr2tecnologia;

import br.com.jr2tecnologia.model.BankAccountPersistenceLocal;
import br.com.jr2tecnologia.model.ConfigPersistenceLocal;
import br.com.jr2tecnologia.model.ModemPersistenceLocal;
import br.com.jr2tecnologia.model.SmsSendPersistenceLocal;
import br.com.jr2tecnologia.model.entity.BankAccount;
import br.com.jr2tecnologia.model.entity.ClientPhone;
import br.com.jr2tecnologia.model.entity.Login;
import br.com.jr2tecnologia.model.entity.Modem;
import br.com.jr2tecnologia.model.entity.SmsSend;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.Date;
import java.util.List;
import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.PostConstruct;
import javax.ejb.ActivationConfigProperty;
import javax.ejb.EJB;
import javax.ejb.MessageDriven;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.ObjectMessage;

/**
 *
 * @author ronildo
 */
@MessageDriven(mappedName = "jms/QueueListenerNowSMS", activationConfig =  {
        @ActivationConfigProperty(propertyName = "acknowledgeMode", propertyValue = "Auto-acknowledge"),
        @ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue")
    })
public class QueueListenerNowSMS implements MessageListener {

    private static final Logger logger = LoggerFactory.getLogger();
    private String nowSmsUrl;
    private String url_str;
    private int maxRetrySendSms;
    private long sleepSendSms;
    private String user_nowsms;
    private String password_nowsms;

    @EJB
    private SmsSendPersistenceLocal smsSendPersistence;
    @EJB
    private ConfigPersistenceLocal configPersistence;
    @EJB
    private ModemPersistenceLocal modemPersistence;
    @EJB
    private BankAccountPersistenceLocal bankAccountPersistence;

    @PostConstruct
    public void initialize(){
        
    }

    public void onMessage(Message message) {
        nowSmsUrl = configPersistence.findConfigByName("now_sms_url").getValue();
        maxRetrySendSms = Integer.parseInt(configPersistence.findConfigByName("max_retry_send_sms").getValue());
        sleepSendSms = Long.parseLong(configPersistence.findConfigByName("sleep_sms_send").getValue());
        user_nowsms = configPersistence.findConfigByName("user_nowsms").getValue();
        password_nowsms = configPersistence.findConfigByName("password_nowsms").getValue();

        ObjectMessage msg = null;
        if (message instanceof ObjectMessage) {
            try {
                msg = (ObjectMessage) message;
                SmsSend smsSend = (SmsSend) msg.getObject();
                Modem modem = findModem(smsSend.getLogin());
                if(modem != null){
                    smsSend.setModem(modem);
                    sendSMS(smsSend);
                }else
                    logger.info("could not find any modem available for this user: " + smsSend.getLogin().getEmail());
            }catch(Exception ex){
                logger.log(Level.SEVERE, "Erro processando mensagem da fila", ex);
            }
        }
    }

    private Modem findModem(Login login){
        List<Modem> modemsOfLogin = modemPersistence.list(new Modem(login, true));
        List<Modem> modemsOfCompany = modemPersistence.list(new Modem(login.getCompany(), true));
        List<Modem> sharedModems = modemPersistence.listAllSharedModems();

        if(!modemsOfLogin.isEmpty())
            return chooseModem(modemsOfLogin);
        else if(!modemsOfCompany.isEmpty())
            return chooseModem(modemsOfCompany);
        else if(!sharedModems.isEmpty())
            return chooseModem(sharedModems);
        else
            return null;
    }

    private Modem chooseModem(List<Modem> modems){
        if(modems.size() > 1){
            return modems.get(new Random().nextInt(modems.size()));
        }else if(modems.size() == 1)
            return modems.get(0);
        else
            return null;
    }
    

    private void setParameter(String name, String value) {
        if (name != null && value != null) {
            url_str += "&" + name + "=";
            try {
                url_str += URLEncoder.encode(value, "UTF-8");
            } catch (UnsupportedEncodingException e) {
                logger.log(Level.SEVERE, "cannot encode this value: " + value, e);
                url_str += value;
            }
        }
    }

    private void makeUrl(SmsSend smsSend){
        
        ClientPhone clientPhone = smsSend.getClientPhone();

        url_str = nowSmsUrl + "?";
        setParameter("user", user_nowsms);
        setParameter("password", password_nowsms);
        setParameter("phonenumber", clientPhone.getPhone().substring(2, clientPhone.getPhone().length()));
        setParameter("text", smsSend.getMessage());
        setParameter("data", null);
        setParameter("udh", null);
        setParameter("pid", null);
        setParameter("dcs", null);
        setParameter("sender", null);
        setParameter("validity", null);
        setParameter("servicetype", null);
        setParameter("smscroute", null);//smsSend.getModem().getSmscRoute());
        setParameter("receiptrequested", "Yes");
        setParameter("sourceport", null);
        setParameter("destport", null);
        setParameter("delayuntil", null);
        setParameter("voicemail", null);
        setParameter("wapurl", null);
        setParameter("wapsl", null);
        logger.info("url to connect Now Sms: " + url_str);
    }

    private String retrieveUrlContent(SmsSend smsSend) {

        HttpURLConnection connection = null;
        BufferedReader in = null;
        makeUrl(smsSend);
        try {
            long start = System.currentTimeMillis();
            URL url = new URL(url_str);
            connection = (HttpURLConnection) url.openConnection();
            connection.setDoOutput(false);
            connection.setDoInput(true);

            String res = connection.getResponseMessage();
            int code = connection.getResponseCode();
            logger.info("successful connection with now sms in '" + (System.currentTimeMillis() - start) + "ms' and response message: '" + res + "'. response code: '" +code+"'");

            if (code == HttpURLConnection.HTTP_OK){
                in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
                String ln = "", reply = "";
                while ((ln = in.readLine()) != null) {
                    reply += ln;
                }
                return reply;
            }
            return null;
        } catch (MalformedURLException ex) {
            logger.log(Level.SEVERE, "Url not correct", ex);
            return null;
        } catch (IOException ex) {
            logger.log(Level.SEVERE, "Cannot connect", ex);
            return null;
        } finally {
            if(in != null)
                try {
                    in.close();
                } catch (IOException ex) {
                    logger.log(Level.SEVERE, "cannot close socket to: " + url_str, ex);
                }
            if(connection != null)
                connection.disconnect();
        }
    }

    private String getMessageID(String reply) {
        if (reply == null) {
            logger.info("sms not sendered. Gateway sms reply message: "+reply);
            return null;
        }else{
            logger.info("sms sent successfully");
            reply = reply.substring(reply.indexOf("MessageID") + 10, reply.indexOf("Recipient") - 2);
            if(reply.length() < 19)
                return reply;
            else
                return "not found";
        }
    }

    private boolean haveCredit(Login login){
        BankAccount bankAccount = login.getBankAccount();

        if(bankAccount.getQuantitySmsBuyed() > 0){
            logger.info("this account " + bankAccount.getPkBankAccount() + " have " + bankAccount.getQuantitySmsBuyed()+ " sms");
            return true;
        }else{
            logger.info("this account " + bankAccount.getPkBankAccount() + " no have credit sms");
            return false;
        }
    }

    private void doDebits(SmsSend smsSend){
        Login login = smsSend.getLogin();
        BankAccount bankAccount = login.getBankAccount();
        bankAccount.setQuantitySmsBuyed( bankAccount.getQuantitySmsBuyed() - 1 );
        bankAccountPersistence.update(bankAccount);
    }

    private void sendSMS(SmsSend smsSend) {
        if(haveCredit(smsSend.getLogin())){
            String reply = retrieveUrlContent(smsSend);
            String nowSmsId = "";
            for (int retry = 0; (nowSmsId = getMessageID(reply)) == null && retry < maxRetrySendSms; retry++){
                try {
                    logger.info("putting thread for send sms in mode sleep: " + sleepSendSms + " ms");
                    Thread.sleep(sleepSendSms);
                } catch (InterruptedException e) {
                    logger.severe("Cannot put thread for sleep");
                }
                reply = retrieveUrlContent(smsSend);
            }

            if(nowSmsId != null && !nowSmsId.equals("fail")){
                smsSend.setNowSmsId(nowSmsId);
                smsSend.setStatus("PROCESSED");
                smsSend.setDatetimeSent(new Date());
                smsSendPersistence.update(smsSend);
                doDebits(smsSend);
                logger.info("sms processed");
            }else{
                smsSend.setStatus("FAILED");
                smsSendPersistence.update(smsSend);
                logger.info("fail to sent sms");
            }
        }else{
            smsSend.setStatus("NO CREDIT");
            smsSendPersistence.update(smsSend);
        }
    }
}